/**
 * Copyright (c) 1997-2005, PostgreSQL Global Development Group
 * Copyright (C) 2007 - 2008 1genia (contact@1genia.com)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the PostgreSQL Global Development Group nor the names
 *    of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 **/
package com.genia.toolbox.config.autoconfiguration.process;

import java.util.Properties;
import java.util.StringTokenizer;

/**
 * Parser for postgres jdbc urls.
 */
public class PostgresUrlParser
{

  /**
   * the property that will contain the server port.
   */
  public static final String PGPORT = "PGPORT";

  /**
   * the property that will contain the server host.
   */
  public static final String PGHOST = "PGHOST";

  /**
   * the property that will contain the serveur database name.
   */
  public static final String PGDBNAME = "PGDBNAME";

  /**
   * the list of protocols that define a postgres url.
   */
  static private String[] protocols = { "jdbc", "postgresql" };



  /**
   * returns the {@link Properties} that describe the connection parameter of
   * the url given in parameter, or <code>null</code> if the url does not
   * correspond to a postgres jdbc url.
   * 
   * @param url
   *          the jdbc url
   * @return the {@link Properties} that describe the connection parameter of
   *         the url given in parameter, or <code>null</code> if the url does
   *         not correspond to a postgres jdbc url
   */
  public static Properties decodeUrl(String url)
  {
    int state = -1;
    Properties urlProps = new Properties();

    String urlServer = url;
    String urlArgs = "";

    int qPos = url.indexOf('?');
    if (qPos != -1) {
      urlServer = url.substring(0, qPos);
      urlArgs = url.substring(qPos + 1);
    }

    // look for an IPv6 address that is enclosed by []
    // the upcoming parsing that uses colons as identifiers can't handle
    // the colons in an IPv6 address.
    int ipv6start = urlServer.indexOf("[");
    int ipv6end = urlServer.indexOf("]");
    String ipv6address = null;
    if (ipv6start != -1 && ipv6end > ipv6start) {
      ipv6address = urlServer.substring(ipv6start + 1, ipv6end);
      urlServer = urlServer.substring(0, ipv6start) + "ipv6host" + urlServer.substring(ipv6end + 1);
    }

    // parse the server part of the url
    StringTokenizer st = new StringTokenizer(urlServer, ":/", true);
    int count;
    for (count = 0; (st.hasMoreTokens()); count++) {
      String token = st.nextToken();

      // PM Aug 2 1997 - Modified to allow multiple backends
      if (count <= 3) {
        if ((count % 2) == 1 && token.equals(":"))
          ;
        else if ((count % 2) == 0) {
          boolean found = (count == 0) ? true : false;
          for (int tmp = 0; tmp < protocols.length; tmp++) {
            if (token.equals(protocols[tmp])) {
              // PM June 29 1997 Added this property to enable the
              // driver
              // to handle multiple backend protocols.
              if (count == 2 && tmp > 0) {
                urlProps.setProperty("Protocol", token);
                found = true;
              }
            }
          }

          if (found == false)
            return null;
        }
        else
          return null;
      }
      else if (count > 3) {
        if (count == 4 && token.equals("/"))
          state = 0;
        else if (count == 4) {
          urlProps.setProperty(PGDBNAME, token);
          state = -2;
        }
        else if (count == 5 && state == 0 && token.equals("/"))
          state = 1;
        else if (count == 5 && state == 0)
          return null;
        else if (count == 6 && state == 1)
          urlProps.setProperty(PGHOST, token);
        else if (count == 7 && token.equals(":"))
          state = 2;
        else if (count == 8 && state == 2) {
          try {
            Integer portNumber = Integer.decode(token);
            urlProps.setProperty(PGPORT, portNumber.toString());
          }
          catch (Exception e) {
            return null;
          }
        }
        else if ((count == 7 || count == 9) && (state == 1 || state == 2) && token.equals("/"))
          state = -1;
        else if (state == -1) {
          urlProps.setProperty(PGDBNAME, token);
          state = -2;
        }
      }
    }
    if (count <= 1) {
      return null;
    }
    // if we extracted an IPv6 address out earlier put it back
    if (ipv6address != null)
      urlProps.setProperty(PGHOST, ipv6address);

    // parse the args part of the url
    StringTokenizer qst = new StringTokenizer(urlArgs, "&");
    for (count = 0; (qst.hasMoreTokens()); count++) {
      String token = qst.nextToken();
      int pos = token.indexOf('=');
      if (pos == -1) {
        urlProps.setProperty(token, "");
      }
      else {
        urlProps.setProperty(token.substring(0, pos), token.substring(pos + 1));
      }
    }

    return urlProps;

  }
}
